{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8cc39cad",
   "metadata": {},
   "source": [
    "# 반복 평가\n",
    "\n",
    "실험에 반복을 추가할 수 있습니다.\n",
    "\n",
    "이는 평가를 여러 번 반복할 수 있게 하여 다음과 같은 경우에 유용합니다:\n",
    "\n",
    "* 더 큰 평가 세트의 경우\n",
    "* 가변적인 응답을 생성할 수 있는 체인의 경우\n",
    "* 가변적인 점수를 생성할 수 있는 평가(예: `llm-as-judge`)의 경우\n",
    "\n",
    "https://docs.smith.langchain.com/how_to_guides/evaluation/evaluate_llm_application#evaluate-on-a-dataset-with-repetitions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34a30a84",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 설치\n",
    "# !pip install -qU langsmith langchain-teddynote"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d75d1492",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "633d9db2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install -qU langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH16-Evaluations\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cd0728e",
   "metadata": {},
   "source": [
    "## RAG 성능 테스트를 위한 함수 정의"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faf11637",
   "metadata": {},
   "outputs": [],
   "source": [
    "from myrag import PDFRAG\n",
    "\n",
    "\n",
    "# 질문에 대한 답변하는 함수를 생성\n",
    "def ask_question_with_llm(llm):\n",
    "    # PDFRAG 객체 생성\n",
    "    rag = PDFRAG(\n",
    "        \"data/SPRI_AI_Brief_2023년12월호_F.pdf\",\n",
    "        llm,\n",
    "    )\n",
    "\n",
    "    # 검색기(retriever) 생성\n",
    "    retriever = rag.create_retriever()\n",
    "\n",
    "    # 체인(chain) 생성\n",
    "    rag_chain = rag.create_chain(retriever)\n",
    "\n",
    "    def _ask_question(inputs: dict):\n",
    "        # 질문에 대한 컨텍스트 검색\n",
    "        context = retriever.invoke(inputs[\"question\"])\n",
    "        # 검색된 문서들을 하나의 문자열로 결합\n",
    "        context = \"\\n\".join([doc.page_content for doc in context])\n",
    "        # 질문, 컨텍스트, 답변을 포함한 딕셔너리 반환\n",
    "        return {\n",
    "            \"question\": inputs[\"question\"],\n",
    "            \"context\": context,\n",
    "            \"answer\": rag_chain.invoke(inputs[\"question\"]),\n",
    "        }\n",
    "\n",
    "    return _ask_question"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d703fa2e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_ollama import ChatOllama\n",
    "\n",
    "\n",
    "gpt_chain = ask_question_with_llm(ChatOpenAI(model=\"gpt-4o-mini\", temperature=1.0))\n",
    "\n",
    "# Ollama 모델을 불러옵니다.\n",
    "ollama_chain = ask_question_with_llm(\n",
    "    ChatOllama(model=\"EEVE-Korean-10.8B:latest\", temperature=1.0)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58ff6c8f",
   "metadata": {},
   "source": [
    "### GPT 모델을 활용한 RAG 에 대하여 반복 평가"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a0e8db9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langsmith.evaluation import evaluate, LangChainStringEvaluator\n",
    "\n",
    "# qa 평가자 생성\n",
    "cot_qa_evalulator = LangChainStringEvaluator(\n",
    "    \"cot_qa\",\n",
    "    config={\"llm\": ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)},\n",
    "    prepare_data=lambda run, example: {\n",
    "        \"prediction\": run.outputs[\"answer\"],\n",
    "        \"reference\": run.outputs[\"context\"],\n",
    "        \"input\": example.inputs[\"question\"],\n",
    "    },\n",
    ")\n",
    "\n",
    "dataset_name = \"RAG_EVAL_DATASET\"\n",
    "\n",
    "# 평가 실행\n",
    "evaluate(\n",
    "    gpt_chain,\n",
    "    data=dataset_name,\n",
    "    evaluators=[cot_qa_evalulator],\n",
    "    experiment_prefix=\"REPEAT_EVAL\",\n",
    "    # 실험 메타데이터 지정\n",
    "    metadata={\n",
    "        \"variant\": \"Repeat 평가를 수행합니다. GPT-4o-mini 모델 (cot_qa)\",\n",
    "    },\n",
    "    num_repetitions=3,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c5f6f91",
   "metadata": {},
   "source": [
    "![](./assets/output-12.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f3e5349",
   "metadata": {},
   "source": [
    "### Ollama 모델을 활용한 RAG 에 대하여 반복 평가"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6852e9e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langsmith.evaluation import evaluate, LangChainStringEvaluator\n",
    "\n",
    "# qa 평가자 생성\n",
    "cot_qa_evalulator = LangChainStringEvaluator(\n",
    "    \"cot_qa\",\n",
    "    config={\"llm\": ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)},\n",
    "    prepare_data=lambda run, example: {\n",
    "        \"prediction\": run.outputs[\"answer\"],\n",
    "        \"reference\": run.outputs[\"context\"],\n",
    "        \"input\": example.inputs[\"question\"],\n",
    "    },\n",
    ")\n",
    "\n",
    "dataset_name = \"RAG_EVAL_DATASET\"\n",
    "\n",
    "# 평가 실행\n",
    "evaluate(\n",
    "    ollama_chain,\n",
    "    data=dataset_name,\n",
    "    evaluators=[cot_qa_evalulator],\n",
    "    experiment_prefix=\"REPEAT_EVAL\",\n",
    "    # 실험 메타데이터 지정\n",
    "    metadata={\n",
    "        \"variant\": \"Repeat 평가를 수행합니다. EEVE-Korean-10.8B 모델 (cot_qa)\",\n",
    "    },\n",
    "    num_repetitions=3,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebbfef6a",
   "metadata": {},
   "source": [
    "![](./assets/output-14.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
